package com.xuxueli.applyModules.commont;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @CLassName RedisConfig
 * @Description redis配置
 **/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public Pool pool() {
        Pool pool = new Pool();
        pool.setMaxActive(1000);
        pool.setMaxIdle(10);
        pool.setMinIdle(5);
        return pool;
    }

    @Bean
    public GenericObjectPoolConfig<?> genericObjectPoolConfig(Pool pool) {
        GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(pool.getMaxActive());
        config.setMaxIdle(pool.getMaxIdle());
        config.setMinIdle(pool.getMinIdle());
        config.setMaxWaitMillis(pool.getMaxWait().toMillis());
        return config;
    }

    @Bean
    public LettuceClientConfiguration getLettuceConfiguration() {
        //开启自适应集群拓扑刷新和周期拓扑刷新
        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
            //开启自适应刷新,自适应刷新不开启,Redis集群变更时将会导致连接异常
            .enableAllAdaptiveRefreshTriggers()
            //自适应刷新超时时间(默认30秒)
            .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
            // 默认关闭,开启定时刷新 ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD
            .enablePeriodicRefresh(Duration.ofSeconds(15)).build();
        ClientOptions clientOptions = ClusterClientOptions.builder()
            .topologyRefreshOptions(clusterTopologyRefreshOptions).build();
        LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
            .poolConfig(genericObjectPoolConfig(redisProperties.getLettuce().getPool()))
            .clientOptions(clientOptions)
            //.commandTimeout(redisProperties.getTimeout())
            .commandTimeout(Duration.ofSeconds(30000))
            .build();
        return lettuceClientConfiguration;
    }

    @Bean
    public RedisSentinelConfiguration sentinelConfiguration() {
        if (redisProperties.getSentinel() == null) {
            return null;
        }
        RedisSentinelConfiguration redisConfig = new RedisSentinelConfiguration();
        redisConfig.setMaster(redisProperties.getSentinel().getMaster());
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            redisConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
        }
        Set<RedisNode> sentinelNode = new HashSet<>();
        for (String sen : redisProperties.getSentinel().getNodes()) {
            String[] arr = sen.split(":");
            sentinelNode.add(new RedisNode(arr[0], Integer.parseInt(arr[1])));
        }
        redisConfig.setSentinels(sentinelNode);
        return redisConfig;
    }

    @Bean
    public RedisClusterConfiguration clusterConfiguration() {
        if (redisProperties.getCluster() == null) {
            return null;
        }
        RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
        List<String> clusterNodes = redisProperties.getCluster().getNodes();
        Set<RedisNode> nodes = new HashSet<>();
        clusterNodes.forEach(address -> nodes
            .add(new RedisNode(address.split(":")[0].trim(), Integer.valueOf(address.split(":")[1]))));
        clusterConfiguration.setClusterNodes(nodes);
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        }
        clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
        return clusterConfiguration;
    }

    @Bean(destroyMethod = "destroy")
    public LettuceConnectionFactory lettuceConnectionFactory() {
        LettuceConnectionFactory lettuceConnectionFactory;
        if (redisProperties.getSentinel() != null) {
            lettuceConnectionFactory = new LettuceConnectionFactory(sentinelConfiguration(), getLettuceConfiguration());
        } else if (redisProperties.getCluster() != null) {
            lettuceConnectionFactory = new LettuceConnectionFactory(clusterConfiguration(), getLettuceConfiguration());
        } else {
            RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(
                redisProperties.getHost(), redisProperties.getPort());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                standaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
            }
            lettuceConnectionFactory = new LettuceConnectionFactory(standaloneConfiguration, getLettuceConfiguration());
        }
        // lettuceConnectionFactory.setShareNativeConnection(false); //是否允许多个线程操作共用同一个缓存连接，默认true，false时每个操作都将开辟新的连接
        // lettuceConnectionFactory.resetConnection(); // 重置底层共享连接, 在接下来的访问时初始化
        return lettuceConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(lettuceConnectionFactory);
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值（默认使用JDK的序列化方式）
        /*Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域，field,get和set,以及修饰符范围，ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型，类必须是非final修饰的，final修饰的类，比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //om.setSerializationInclusion(Include.NON_NULL);
        //om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jacksonSeial.setObjectMapper(om);*/
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
        // 值采用json序列化
        template.setValueSerializer(fastJsonRedisSerializer);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(fastJsonRedisSerializer);
        //template.afterPropertiesSet();
        // 启用默认序列化方式
        //template.setEnableDefaultSerializer(true);
        //template.setDefaultSerializer(jacksonSeial);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 对hash类型的数据操作
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * 对redis字符串类型数据操作
     */
    @Bean
    public ValueOperations valueOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForValue();
    }

    /**
     * 对链表类型的数据操作
     */
    @Bean
    public ListOperations<String, String> listOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * 对无序集合类型的数据操作
     */
    @Bean
    public SetOperations setOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForSet();
    }

    /**
     * 对有序集合类型的数据操作
     */
    @Bean
    public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}
